package org.jeecg.modules.gis.util;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.beanutils.BeanUtils;
import org.geotools.geojson.geom.GeometryJSON;
import org.geotools.geometry.jts.WKBReader;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.ParseException;

import java.io.IOException;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;
import java.sql.Blob;
import java.sql.SQLException;
import java.sql.Struct;
import java.util.List;
import java.util.Map;

public class GeoJsonJavaBeanUtil {

    public static <T> JSONObject beanToFeature(T srcBean)
            throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        JSONObject featureJson = new JSONObject();
        featureJson.put("type", "Feature");

        JSONObject properties = new JSONObject();
        JSONObject geometryJson = new JSONObject();

        Geometry geo = null;
        Field[] fields = srcBean.getClass().getDeclaredFields();

        for (Field field : fields) {
            int modifiers = field.getModifiers();
            if (Modifier.isStatic(modifiers) || Modifier.isTransient(modifiers)) {
                continue;
            }

            field.setAccessible(true);
            Object value = field.get(srcBean);

            if (Geometry.class.isAssignableFrom(field.getType())) {
                geo = (Geometry) value;
                continue;
            }

            if (value == null) continue;

            if (value instanceof List<?>) {
                String jsonArrayStr = JSON.toJSONString(value);
                properties.put(field.getName(), JSON.parseArray(jsonArrayStr));
            } else {
                Object nestedPropertyValue = BeanUtils.getNestedProperty(srcBean, field.getName());
                if (nestedPropertyValue != null) {
                    properties.put(field.getName(), nestedPropertyValue);
                }
            }
        }

        if (geo != null) {
            String type = geo.getGeometryType();
            geometryJson.put("type", type);

            GeometryJSON geometryJSON = new GeometryJSON(7);
            StringWriter sw = new StringWriter();
            geometryJSON.write(geo, sw);
            JSONObject geoJson = JSONObject.parseObject(sw.toString());

            geometryJson.put("coordinates", geoJson.get("coordinates"));
            featureJson.put("geometry", geometryJson);
        } else {
            featureJson.put("geometry", null);
        }

        featureJson.put("properties", properties);
        return featureJson;
    }

    public static JSONObject mapToFeature(Map<String, Object> c1) throws IOException, ParseException, SQLException {
        JSONObject featureJson = new JSONObject();
        featureJson.put("type", "Feature");
        JSONObject properties = new JSONObject();
        JSONObject geometryJson = new JSONObject();

        for (Map.Entry<String, Object> entry : c1.entrySet()) {
            if ("geom".equalsIgnoreCase(entry.getKey())) {
                Object value = entry.getValue();
                if (value instanceof Struct) {
                    Struct struct = (Struct) value;
                    Object[] attrs = struct.getAttributes();

                    Blob blob = (Blob) attrs[1];
                    byte[] wkb = blob.getBytes(1, (int) blob.length());

                    WKBReader wkbReader = new WKBReader();
                    Geometry geometry = wkbReader.read(wkb);

                    String type = geometry.getGeometryType();
                    geometryJson.put("type", type);

                    GeometryJSON geometryJSON = new GeometryJSON(7);
                    StringWriter sw = new StringWriter();
                    geometryJSON.write(geometry, sw);
                    JSONObject geoJson = JSONObject.parseObject(sw.toString());

                    geometryJson.put("coordinates", geoJson.get("coordinates"));
                    featureJson.put("geometry", geometryJson);
                }
            } else {
                properties.put(entry.getKey(), entry.getValue());
            }
        }

        featureJson.put("properties", properties);
        return featureJson;
    }

    public static <T> JSONObject beanToProperties(T srcBean)
            throws IOException, InvocationTargetException, IllegalAccessException, NoSuchMethodException {
        JSONObject properties = new JSONObject();
        Field[] fields = srcBean.getClass().getDeclaredFields();

        for (Field field : fields) {
            if (Geometry.class.isAssignableFrom(field.getType())) {
                continue;
            }
            properties.put(field.getName(), BeanUtils.getNestedProperty(srcBean, field.getName()));
        }

        return properties;
    }
}
